經過前面幾天的介紹,我們認識了許多常使用的 hooks,不過除了那些 hooks 之外,我們也可以將一些常用的共同程式邏輯抽取出來寫一個函式,這就是 Custom Hook。
在了解 Custom Hook 後,我們來實做一個 Custom Hook,這個 hook 是一個用來呼叫 Api 並進行一些處理的 Custom Hook,名字就叫 useAxios。
2022/1/26 更新 註: 這裡只是示範如果做一個 Custom hook,在呼叫 api 還有其他的做法,例如 redux dispatch async action + middleware,讀者可以自行依照狀況判斷使用~
在 codesandbox 新增一個檔案 useAxios,引入 axios 套件後,我們要使用 JSONplaceholder 的 api 作為 Base URL,等這個 hook 完成後會使用它來呼叫 JSONplaceholder 的 api。
在 useAxios 檔案我們宣告一個函式 useAxios,在做呼叫 api 時我們也會需要對一些像是呼叫 api 的載入時間和錯誤訊息做處理,所以建立兩個 state: isLoading 和 error,並且建立一個發出請求的函式 sendRequest,在這個函式內會對呼叫的 api 做處理,最後回傳一個包含 isLoading、error 和 sendRequest 的物件。
import { useState, useCallback } from "react";
import axios from "axios";
axios.defaults.baseURL = "https://jsonplaceholder.typicode.com";
const useAxios = () => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const sendRequest = useCallback(() => {}, []);
return {
isLoading,
error,
sendRequest,
};
};
export default useAxios;
在 sendRequest 中,我們會傳入兩個參數 requestConfig 和 applyData,因為在呼叫每隻 api 的路徑、http 方法和其他參數都不太一樣,所以透過 requestConfig 可以將這些資訊帶入到 sendRequest 裡面,處理完後透過 callback function 也就是第二個參數 applyData 將呼叫 api 的結果回傳。
完成後的程式碼如下:
import { useState, useCallback } from 'react';
import axios from 'axios';
axios.defaults.baseURL = "https://jsonplaceholder.typicode.com";
const useAxios = () => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const sendRequest = useCallback(async (requestConfig, applyData) => {
setIsLoading(true);
setError(null);
let res;
try {
res = await axios({
url: requestConfig.url,
method: requestConfig.method ? requestConfig.method : 'GET',
headers: requestConfig.headers ? requestConfig.headers : {},
data: requestConfig.data ? requestConfig.data : null,
});
} catch (error) {
setError(error.message || 'Something went wrong!');
} finally {
if (res) {
applyData(res.data);
}
setIsLoading(false);
}
}, []);
return {
isLoading,
error,
sendRequest,
};
};
export default useAxios;
在 App 元件中呼叫幾支 JSONplaceholder 的 api。
import { useEffect, useState } from "react";
import useAxios from "./useAxios";
export default function App() {
const [data, setData] = useState([]);
const { isLoading, error, sendRequest: fetchData } = useAxios();
const { sendRequest: createData } = useAxios();
useEffect(() => {
fetchData({ url: "/posts" }, (res) => {
console.log(res);
setData(res);
});
}, []);
const clickNewData = () => {
createData(
{
url: "/posts",
method: "POST",
data: {
// id 沒加是因為 JSONPlaceholder 只做假新增不會更新到資料庫,所以 JSONPlaceholder 會自動產生一個假的 id
userId: 10,
title: "新增的物件",
body: "example"
}
},
(res) => console.log(res)
);
};
if (isLoading) return <h1>Loading...</h1>;
if (error) return <h1>{error}</h1>;
return (
<>
<ul>
{data.map((item) => (
<li key={item.id}>
<p>{item.title}</p>
</li>
))}
</ul>
<button onClick={clickNewData}>新增</button>
</>
);
}
這次的實作程式碼在以下連結,另外附上 JSONplaceholder 的官網連結:
這是我在閱讀 React Docs 看到的,useCallback 的文件有提到推薦為 custom Hook 內的函式加上 useCallback。